{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "5c99737c",
   "metadata": {},
   "source": [
    "# 习题\n",
    "## 习题18.3\n",
    "![image.png](./images/exercise3.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "165e2486",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "参数P(w_i|z_k)：\n",
      "[[0.115 0.107 0.   ]\n",
      " [0.    0.    0.154]\n",
      " [0.23  0.    0.   ]\n",
      " [0.    0.    0.154]\n",
      " [0.    0.107 0.077]\n",
      " [0.424 0.248 0.231]\n",
      " [0.    0.215 0.   ]\n",
      " [0.    0.    0.154]\n",
      " [0.    0.    0.231]\n",
      " [0.    0.322 0.   ]\n",
      " [0.23  0.    0.   ]]\n",
      "参数P(z_k|d_j)：\n",
      "[[0.    1.    0.    1.    1.    0.    0.    0.562 0.   ]\n",
      " [1.    0.    1.    0.    0.    0.    0.    0.438 0.   ]\n",
      " [0.    0.    0.    0.    0.    1.    1.    0.    1.   ]]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "\n",
    "class EMPlsa:\n",
    "    def __init__(self, max_iter=100, random_state=2024):\n",
    "        \"\"\"\n",
    "        基于生成模型的EM算法的概率潜在语义模型\n",
    "        :param max_iter: 最大迭代次数\n",
    "        :param random_state: 随机种子\n",
    "        \"\"\"\n",
    "        self.max_iter = max_iter\n",
    "        self.random_state = random_state\n",
    "\n",
    "    def fit(self, X, K):\n",
    "        \"\"\"5\n",
    "        :param X: 单词-文本矩阵\n",
    "        :param K: 话题个数\n",
    "        :return: P(w_i|z_k) 和 P(z_k|d_j)\n",
    "        \"\"\"\n",
    "        # M, N分别为单词个数和文本个数\n",
    "        M, N = X.shape\n",
    "\n",
    "        # 计算n(d_j)\n",
    "        n_d = [np.sum(X[:, j]) for j in range(N)]\n",
    "\n",
    "        # (1)设置参数P(w_i|z_k)和P(z_k|d_j)的初始值\n",
    "        np.random.seed(self.random_state)\n",
    "        p_wz = np.random.random((M, K))\n",
    "        p_zd = np.random.random((K, N))\n",
    "\n",
    "        # (2)迭代执行E步和M步，直至收敛为止\n",
    "        for _ in range(self.max_iter):\n",
    "            # E步\n",
    "            P = np.zeros((M, N, K))\n",
    "            for i in range(M):\n",
    "                for j in range(N):\n",
    "                    for k in range(K):\n",
    "                        P[i][j][k] = p_wz[i][k] * p_zd[k][j]\n",
    "                    P[i][j] /= np.sum(P[i][j])\n",
    "\n",
    "            # M步\n",
    "            for k in range(K):\n",
    "                for i in range(M):\n",
    "                    p_wz[i][k] = np.sum([X[i][j] * P[i][j][k] for j in range(N)])\n",
    "                p_wz[:, k] /= np.sum(p_wz[:, k])\n",
    "\n",
    "            for k in range(K):\n",
    "                for j in range(N):\n",
    "                    p_zd[k][j] = np.sum([X[i][j] * P[i][j][k] for i in range(M)]) / n_d[j]\n",
    "\n",
    "        return p_wz, p_zd\n",
    "\n",
    "    \n",
    "# 输入文本-单词矩阵，共有9个文本，11个单词\n",
    "X = np.array([[0, 0, 1, 1, 0, 0, 0, 0, 0],\n",
    "              [0, 0, 0, 0, 0, 1, 0, 0, 1],\n",
    "              [0, 1, 0, 0, 0, 0, 0, 1, 0],\n",
    "              [0, 0, 0, 0, 0, 0, 1, 0, 1],\n",
    "              [1, 0, 0, 0, 0, 1, 0, 0, 0],\n",
    "              [1, 1, 1, 1, 1, 1, 1, 1, 1],\n",
    "              [1, 0, 1, 0, 0, 0, 0, 0, 0],\n",
    "              [0, 0, 0, 0, 0, 0, 1, 0, 1],\n",
    "              [0, 0, 0, 0, 0, 2, 0, 0, 1],\n",
    "              [1, 0, 1, 0, 0, 0, 0, 1, 0],\n",
    "              [0, 0, 0, 1, 1, 0, 0, 0, 0]])\n",
    "\n",
    "# 设置精度为3\n",
    "np.set_printoptions(precision=3, suppress=True)\n",
    "\n",
    "# 假设话题的个数是3个\n",
    "k = 3\n",
    "\n",
    "em_plsa = EMPlsa(max_iter=100)\n",
    "\n",
    "p_wz, p_zd = em_plsa.fit(X, 3)\n",
    "\n",
    "print(\"参数P(w_i|z_k)：\")\n",
    "print(p_wz)\n",
    "print(\"参数P(z_k|d_j)：\")\n",
    "print(p_zd)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
